package com.ikingtech.framework.sdk.message.embedded;

import com.ikingtech.framework.sdk.enums.message.MessageReceiverTypeEnum;
import com.ikingtech.framework.sdk.message.embedded.annotation.*;
import com.ikingtech.framework.sdk.message.model.rpc.*;
import com.ikingtech.framework.sdk.utils.Tools;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.Ordered;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import static com.ikingtech.framework.sdk.context.constant.CommonConstants.MESSAGE_TEMPLATE_BEAN_DEFINITION_REPORT_RUNNER_ORDER;
import static com.ikingtech.framework.sdk.context.constant.SecurityConstants.DEFAULT_TENANT_CODE;

/**
 * @author tie yan
 */
@Slf4j
@RequiredArgsConstructor
public class MessageTemplateBeanDefinitionFactory implements ApplicationRunner, ApplicationContextAware, Ordered {

    private final MessageTemplateBeanDefinitionReporter reporter;

    private ApplicationContext applicationContext;

    protected static final Map<String, MessageTemplateBeanDefinition> MESSAGE_TEMPLATE_BEAN_DEFINITION_MAP = new HashMap<>();

    @Override
    public int getOrder() {
        return MESSAGE_TEMPLATE_BEAN_DEFINITION_REPORT_RUNNER_ORDER;
    }

    @Override
    public void run(ApplicationArguments args) {
        Map<String, Object> messageTemplateMap = this.applicationContext.getBeansWithAnnotation(MessageTemplate.class);
        if (Tools.Coll.isBlankMap(messageTemplateMap)) {
            return;
        }

        for (Object messageTemplate : messageTemplateMap.values()) {
            MessageTemplate messageTemplateAnnotation = messageTemplate.getClass().getAnnotation(MessageTemplate.class);
            MessageTemplateBeanDefinition templateBeanDefinition = new MessageTemplateBeanDefinition();
            templateBeanDefinition.setTenantCode(Tools.Str.isBlank(messageTemplateAnnotation.tenant()) ? DEFAULT_TENANT_CODE : messageTemplateAnnotation.tenant());
            templateBeanDefinition.setBusinessKey(messageTemplateAnnotation.businessKey());
            templateBeanDefinition.setBusinessName(messageTemplateAnnotation.businessName());
            templateBeanDefinition.setMessageTemplateKey(messageTemplateAnnotation.key());
            templateBeanDefinition.setMessageTemplateTitle(messageTemplateAnnotation.name());
            templateBeanDefinition.setAllUser(messageTemplateAnnotation.all());
            templateBeanDefinition.setInternal(messageTemplateAnnotation.internal());
            templateBeanDefinition.setChannelDefinitions(new ArrayList<>());
            if (Tools.Array.isNotBlank(messageTemplateAnnotation.channel())) {
                for (MessageChannel messageChannel : messageTemplateAnnotation.channel()) {
                    MessageChannelDefinition channelDefinition = new MessageChannelDefinition();
                    channelDefinition.setChannel(messageChannel.type());
                    channelDefinition.setChannelId(messageChannel.id());
                    channelDefinition.setChannelName(messageChannel.name());
                    channelDefinition.setChannelTemplateId(messageChannel.templateId());
                    channelDefinition.setShowNotification(messageChannel.showNotification());
                    channelDefinition.setTemplateContent(messageChannel.content());
                    templateBeanDefinition.getChannelDefinitions().add(channelDefinition);
                    if (Tools.Array.isNotBlank(messageChannel.redirect())) {
                        channelDefinition.setRedirectDefinitions(Tools.Array.convertList(messageChannel.redirect(), redirect -> {
                            MessageRedirectDefinition messageRedirectDefinition = new MessageRedirectDefinition();
                            messageRedirectDefinition.setRedirectTo(redirect.link());
                            messageRedirectDefinition.setRedirectCode(Tools.Str.isBlank(redirect.code()) ? "DEFAULT" : redirect.code());
                            messageRedirectDefinition.setRedirectName(redirect.linkName());
                            messageRedirectDefinition.setSortOrder(redirect.order());
                            return messageRedirectDefinition;
                        }));
                    }
                }
            }
            templateBeanDefinition.setParamDefinitions(new ArrayList<>());
            templateBeanDefinition.setReceiverDefinitions(new ArrayList<>());
            Field[] declaredFields = messageTemplate.getClass().getDeclaredFields();
            this.dealMessageTemplateField(declaredFields, templateBeanDefinition);
            MESSAGE_TEMPLATE_BEAN_DEFINITION_MAP.put(messageTemplateAnnotation.key(), templateBeanDefinition);
        }
        this.reporter.run();
    }

    private void dealMessageTemplateField(Field[] declaredFields, MessageTemplateBeanDefinition templateBeanDefinition) {
        for (Field declaredField : declaredFields) {
            MessageParam paramAnnotation = declaredField.getAnnotation(MessageParam.class);
            if (null != paramAnnotation) {
                this.parseParam(templateBeanDefinition, declaredField, paramAnnotation);
            }
            this.parseReceiver(templateBeanDefinition, declaredField);
        }
    }

    private void parseParam(MessageTemplateBeanDefinition templateBeanDefinition, Field declaredField, MessageParam fieldAnnotation) {
        MessageParamDefinition messageParamDefinition = new MessageParamDefinition();
        messageParamDefinition.setParamName(declaredField.getName());
        messageParamDefinition.setParamDescription(fieldAnnotation.description());
        templateBeanDefinition.getParamDefinitions().add(messageParamDefinition);
    }

    private void parseReceiver(MessageTemplateBeanDefinition templateBeanDefinition, Field declaredField) {
        MessageReceiverDefinition receiverDefinition = new MessageReceiverDefinition();
        receiverDefinition.setReceiverParamName(declaredField.getName());
        UserReceiver userReceiver = declaredField.getAnnotation(UserReceiver.class);
        if (null != userReceiver) {
            receiverDefinition.setReceiverType(MessageReceiverTypeEnum.USER);
            templateBeanDefinition.getReceiverDefinitions().add(receiverDefinition);
        }
        RoleReceiver roleReceiver = declaredField.getAnnotation(RoleReceiver.class);
        if (null != roleReceiver) {
            receiverDefinition.setReceiverType(MessageReceiverTypeEnum.ROLE);
            templateBeanDefinition.getReceiverDefinitions().add(receiverDefinition);
        }
        PostReceiver postReceiver = declaredField.getAnnotation(PostReceiver.class);
        if (null != postReceiver) {
            receiverDefinition.setReceiverType(MessageReceiverTypeEnum.POST);
            templateBeanDefinition.getReceiverDefinitions().add(receiverDefinition);
        }
        DeptReceiver deptReceiver = declaredField.getAnnotation(DeptReceiver.class);
        if (null != deptReceiver) {
            receiverDefinition.setReceiverType(MessageReceiverTypeEnum.DEPARTMENT);
            templateBeanDefinition.getReceiverDefinitions().add(receiverDefinition);
        }
    }

    @Override
    public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
